/*
 * $Id: uneval.js,v 0.3 2009/02/26 02:29:58 dankogai Exp dankogai $
 */
if (typeof (this['uneval']) !== 'function') {
	var hasOwnProperty = Object.prototype.hasOwnProperty;
	var protos = [];
	var char2esc = {
		'\t' : 't',
		'\n' : 'n',
		'\v' : 'v',
		'\f' : 'f',
		'\r' : '\r',
		'\'' : '\'',
		'\"' : '\"',
		'\\' : '\\'
	};
	var escapeChar = function(c) {
		if (c in char2esc)
			return '\\' + char2esc[c];
		var ord = c.charCodeAt(0);
		return ord < 0x20 ? '\\x0' + ord.toString(16) : ord < 0x7F ? '\\' + c
				: ord < 0x100 ? '\\x' + ord.toString(16)
						: ord < 0x1000 ? '\\u0' + ord.toString(16) : '\\u'
								+ ord.toString(16)
	};
	var uneval_asis = function(o) {
		return o.toString()
	};
	/* predefine objects where typeof(o) != 'object' */
	var name2uneval = {
		'boolean' : uneval_asis,
		'number' : uneval_asis,
		'string' : function(o) {
			return '\''
					+ o.toString().replace(/[\x00-\x1F\'\"\\\u007F-\uFFFF]/g,
							escapeChar) + '\''
		},
		'undefined' : function(o) {
			return 'undefined'
		},
		'function' : uneval_asis
	};

	var uneval_default = function(o, np) {
		var src = []; // a-ha!
		for ( var p in o) {
			if (!hasOwnProperty.call(o, p))
				continue;
			src[src.length] = uneval(p) + ':' + uneval(o[p], 1);
		}
		;
		// parens needed to make eval() happy
		return np ? '{' + src.toString() + '}' : '({' + src.toString() + '})';
	};

	uneval_set = function(proto, name, func) {
		protos[protos.length] = [ proto, name ];
		name2uneval[name] = func || uneval_default;
	};

	uneval_set(Array, 'array', function(o) {
		var src = [];
		for ( var i = 0, l = o.length; i < l; i++)
			src[i] = uneval(o[i]);
		return '[' + src.toString() + ']';
	});
	uneval_set(RegExp, 'regexp', uneval_asis);
	uneval_set(Date, 'date', function(o) {
		return '(new Date(' + o.valueOf() + '))';
	});

	var typeName = function(o) {
		// if (o === null) return 'null';
		var t = typeof o;
		if (t != 'object')
			return t;
		// we have to lenear-search. sigh.
		for ( var i = 0, l = protos.length; i < l; i++) {
			if (o instanceof protos[i][0])
				return protos[i][1];
		}
		return 'object';
	};

	uneval = function(o, np) {
		// if (o.toSource) return o.toSource();
		if (o === null)
			return 'null';
		var func = name2uneval[typeName(o)] || uneval_default;
		return func(o, np);
	}
}

if (typeof (this['clone']) !== 'function') {
	clone = function(o) {
		try {
			return eval(uneval(o));
		} catch (e) {
			throw (e);
		}
	};
}

//var o = {
//'boolean'  :false,
//'number'   :1,
//'string'   :'Can\'t you\n\t"uneval" this? façade\\漢字',
//'null'     :null,
//'undefined':undefined,
//'function' :function(o){ return o },
//'array'    :[0,1,2,3],
//'regexp'   :/[0-9A-Za-z]+/g,
//'date'     :new Date(),
//'object'   :{zero:0,one:1},
//'json'     :[{zero:0,one:1}],
//'json2'    :[{"zero":"0","one":"1"},{"two":"2","three":"3"},{"leaf":false}]
//};
//console.log(uneval(o['boolean']));
//console.log(uneval(o['number']));
//console.log(uneval(o['string']));
//console.log(uneval(o['undefined']));
//console.log(uneval(o['null']));
//console.log(uneval(o['function']));
//console.log(uneval(o['array']));
//console.log(uneval(o['regexp']));
//console.log(uneval(o['date']));
//console.log(uneval(o['object']));
//console.log(uneval(o['json']));
//console.log(uneval(o['json2']));
//console.log(uneval(o));
//console.log(uneval(o, 1));
//var o2 = eval(uneval(o));
//console.log(o2);
//console.log(uneval(o2));
//o2 = clone(o);
//o2['number']++;
//console.log(uneval(o2));
//console.log(uneval(o));